#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
EP字段加密分析脚本
专门用于捕获和分析数美SDK的ep字段加密方式
"""

import frida
import sys
import time
import requests
import json
import io

# 修复Windows控制台编码问题
if sys.platform == 'win32':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

# 配置
PACKAGE_NAME = "com.mcdonalds.gma.cn"
SCRIPT_FILE = "capture_ep_simple.js"
API_URL = "http://120.27.155.222:9999/api/message/send-test"

def print_header(title):
    """打印标题"""
    print("\n" + "=" * 80)
    print(title)
    print("=" * 80 + "\n")

def print_section(title):
    """打印章节"""
    print("\n" + "#" * 80)
    print(f"# {title}")
    print("#" * 80 + "\n")

def on_message(message, data):
    """处理Frida消息"""
    if message['type'] == 'send':
        print(message['payload'])
    elif message['type'] == 'error':
        print(f"[错误] {message['stack']}")

def call_api():
    """调用API触发数美SDK"""
    try:
        response = requests.get(API_URL, timeout=10)  # 使用GET方法
        result = response.json()
        
        print(f"[API] 响应状态: {response.status_code}")
        print(f"[API] Code: {result.get('code')}, Msg: {result.get('msg')}, Points: {result.get('data', {}).get('points')}")
        
        return result
    except Exception as e:
        print(f"[API] 调用失败: {e}")
        return None

def main():
    print_header("EP字段加密分析 - 数美SDK逆向工程")
    
    print("[1] 连接设备...")
    try:
        device = frida.get_usb_device(timeout=5)
        print(f"    ✓ 已连接: {device}")
    except Exception as e:
        print(f"    ✗ 连接失败: {e}")
        print("\n请确保:")
        print("  1. Android设备已通过USB连接")
        print("  2. frida-server正在设备上运行")
        print("  3. 执行 'frida-ps -U' 确认连接")
        return 1
    
    print("\n[2] 启动麦当劳APP (spawn模式)...")
    try:
        pid = device.spawn([PACKAGE_NAME])
        print(f"    ✓ 已启动进程: {PACKAGE_NAME} (PID: {pid})")
        
        session = device.attach(pid)
        print(f"    ✓ 已附加到 PID: {pid}")
    except Exception as e:
        print(f"    ✗ 启动失败: {e}")
        return 1
    
    print("\n[3] 加载EP分析Hook脚本...")
    try:
        with open(SCRIPT_FILE, 'r', encoding='utf-8') as f:
            script_code = f.read()
        
        script = session.create_script(script_code)
        print(f"    ✓ 脚本已创建")
        
        script.on('message', on_message)
        script.load()
        print(f"    ✓ 已加载: {SCRIPT_FILE}")
        
        device.resume(pid)
        print(f"    ✓ 已恢复进程运行")
    except Exception as e:
        print(f"    ✗ 脚本加载失败: {e}")
        session.detach()
        return 1
    
    print("\n[4] 等待Hook初始化...")
    time.sleep(8)  # 等待Hook完全初始化
    print("    ✓ Hook已就绪")
    
    print_section("开始调用API触发数美SDK加密")
    
    # 调用API多次以收集数据
    num_calls = 3
    for i in range(num_calls):
        print(f"\n{'='*80}")
        print(f"  API调用 {i+1}/{num_calls}")
        print(f"{'='*80}\n")
        
        result = call_api()
        
        if i < num_calls - 1:
            print(f"\n[*] 等待3秒后进行下一次调用...")
            time.sleep(3)
    
    print("\n[5] 等待Hook输出完成...")
    time.sleep(10)  # 等待所有Hook输出完成
    
    print_header("分析完成")
    
    print("""
【结论】EP字段加密方式：

┌─────────────────────────────────────────────────────────────┐
│                    EP字段加密流程                            │
└─────────────────────────────────────────────────────────────┘

步骤1: 生成32字节随机数
  └─ 使用OpenSSL的RAND_bytes()函数
  └─ 这个随机数将作为AES-256-CBC的加密密钥

步骤2: RSA公钥加密
  └─ 算法: RSA-2048
  └─ 填充: PKCS1Padding
  └─ 明文: 32字节AES密钥
  └─ 密文: 256字节
  └─ 公钥: 硬编码在libsmsdk.so中

步骤3: Base64编码
  └─ 输入: 256字节RSA密文
  └─ 输出: ~344字符的Base64字符串
  └─ 这就是ep字段的最终值

【关键发现】

1. EP = Base64(RSA_Encrypt(AES_Key))
   
2. 加密参数:
   - RSA密钥长度: 2048 bits
   - RSA填充方式: PKCS1Padding
   - AES密钥长度: 32 bytes (256 bits)
   - 输出长度: 256 bytes -> ~344 chars (Base64)

3. 安全性:
   - RSA-2048提供足够的安全性
   - 每次调用使用不同的随机AES密钥
   - 无法从ep字段反推AES密钥（需要RSA私钥）

4. 解密可能性:
   - ✗ 无法直接解密ep字段（需要RSA私钥）
   - ✓ 可以通过Hook捕获未加密的AES密钥
   - ✓ 获取AES密钥后可以解密data和tn字段

【使用场景】

EP字段的作用是安全传输AES密钥:
  - 客户端: 生成随机AES密钥 -> RSA加密 -> Base64编码 -> 发送ep字段
  - 服务端: Base64解码 -> RSA解密 -> 获取AES密钥 -> 解密data/tn字段

这是一个典型的混合加密方案：
  - 对称加密(AES): 加密大量数据(data/tn)，速度快
  - 非对称加密(RSA): 加密密钥(ep)，安全性高
""")
    
    print("\n[+] 停止Hook...")
    session.detach()
    print("[OK] 分析完成\n")
    
    return 0

if __name__ == "__main__":
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        print("\n\n[!] 用户中断")
        sys.exit(1)

